import { Stage } from 'babylon-lib/base/stage';
import { AbstractCharacter } from '../../interactive-object/character/abstract-character';
import { Character } from '../../interactive-object/character/character';
import { CharacterController } from '../../interactive-object/character/character-controller/character-controller';
import { MultiplayerManager } from './multiplayer-manager';
import { MMO_MESSAGE_TYPE } from '../common';
import { Vector3 } from '@babylonjs/core';
import { IPlayerInfo } from '../mmo-player';


class CharacterManager {

    public static readonly other: Map<string, AbstractCharacter> = new Map();

    private static _stage: Stage;
    private static _self: AbstractCharacter | null = null;
    private static _controller: CharacterController | null = null;
    private static _tv0 = new Vector3(0, 0, 0);

    public static initialize(stage: Stage): void {
        this._stage = stage;
    }

    public static get self(): AbstractCharacter | null {
        if (!this._self) {
            console.warn('未指定当前玩家角色！');
            return null;
        }
        return this._self!;
    }

    public static defineSelf(character: AbstractCharacter): void {
        this._self = character;
        this._self.onActionChange((index: number, loop: boolean) => {
            if (MultiplayerManager.room) MultiplayerManager.room.send(MMO_MESSAGE_TYPE.PLAYER_ACTION, { index: index, loop: loop });
        });
        this._self.onVisibleChange((visible: boolean) => {
            if (MultiplayerManager.room) MultiplayerManager.room.send(MMO_MESSAGE_TYPE.PLAYER_VISIBLE, { value: visible });
        });
    }

    public static defineController(controller: CharacterController): void {
        this._controller = controller;
        this._controller.onTranslate((x: number, y: number, z: number) => {
            if (MultiplayerManager.room) MultiplayerManager.room.send(MMO_MESSAGE_TYPE.PLAYER_POSITION, { x: x, y: y, z: z });
        });
        this._controller.onRotate((x: number, y: number, z: number) => {
            if (MultiplayerManager.room) MultiplayerManager.room.send(MMO_MESSAGE_TYPE.PLAYER_ROTATION, { x: x, y: y, z: z });
        });
        this._controller.onScale((x: number, y: number, z: number) => {
            if (MultiplayerManager.room) MultiplayerManager.room.send(MMO_MESSAGE_TYPE.PLAYER_SCALE, { x: x, y: y, z: z });
        });
    }

    public static async addPlayer(sessionId: string, player: IPlayerInfo): Promise<AbstractCharacter> {
        const scope = CharacterManager;
        return new Promise((resolve, reject) => {
            Character.create(scope._stage, {
                id: sessionId,
                name: player.name,
                modelUrl: player.outfit
            }).then((other) => {
                resolve(other);
                scope.other.set(sessionId, other);
                const offset = scope._controller ? scope._controller.characterOffset : new Vector3(0, 0, 0);
                other.container.position.set(offset.x + player.x, offset.y + player.y, offset.z + player.z);
                other.setDirection(new Vector3(player.rx, player.ry, player.rz));
                other.playAction(player.actionIndex, player.actionLoop);
            }, (reason) => {
                reject(reason);
            });

        });
    }

    public static removePlayer(sessionId: string): void {
        if (!this.other.has(sessionId)) return;
        const c = this.other.get(sessionId);
        if (c) c.dispose();
        this.other.delete(sessionId);
    }

    public static setCharacterPosition(sessionId: string, x: number, y: number, z: number): void {
        const c = this.getOtherCharacter(sessionId);
        if (!c) return;
        const offset = this._controller!.characterOffset;
        c.container.position.set(offset.x + x, offset.y + y, offset.z + z);
    }

    public static setCharacterRotation(sessionId: string, x: number, y: number, z: number): void {
        const c = this.getOtherCharacter(sessionId);
        if (!c) return;
        this._tv0.set(x, y, z);
        c.setDirection(this._tv0);
    }

    public static setCharacterScale(sessionId: string, x: number, y: number, z: number): void {
        const c = this.getOtherCharacter(sessionId);
        if (!c) return;
        c.container.scaling.set(x, y, z);
    }

    public static setCharacterAction(sessionId: string, index: number, loop: boolean): void {
        const c = this.getOtherCharacter(sessionId);
        if (!c) return;
        c.playAction(index, loop);
    }

    public static setCharacterVisible(sessionId: string, visible: boolean): void {
        const c = this.getOtherCharacter(sessionId);
        if (!c) return;
        c.isVisible = visible;
    }

    private static getOtherCharacter(sessionId: string): AbstractCharacter | null {
        if (!this.other.has(sessionId)) return null;
        const c = this.other.get(sessionId);
        if (!c) return null;
        return c;
    }

}


export { CharacterManager };